package RofMission;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import main.PlanesProp;
import missObjects.AirfieldsModelList;
import missObjects.AirplaneUse;
import missObjects.MCUType;
import missObjects.RofMissAirplane;
import missObjects.RofMissObjAirfield;
import missObjects.RofMissObjBlock;
import missObjects.RofMissObjGroup;
import missObjects.RofMissObjMCU;
import missionLog.MissionLog;
import db.RofDB;
import db.RofObjRef;

/**
 * @author lefuneste
 * Handle RoF mission creation and writing
 *
 */
public class RofMission {
	//mission attributes
	public static String time="12:30:0";
	public static String date="10.10.1914";
	public static String season="su";
	public static int cloudLevel=1500;
	public static int cloudHeight=600;
	public static int precLevel=0;
	public static int precType=0;
	public static int turbulence=0;
	public static int tempPressLevel=0;
	public static int temperature = 15;
	public static int pressure = 760;
	// {direction, speed}
	public static int[] windLayers0={187,0};
	public static int[] windLayers500={27,0};
	public static int[] windLayers1000={27,0};
	
	//mission building variables
	private static List<Integer> listAirplanes;
	private static List<Integer> listMCUtoActivate;
	private static int currentIndex=0;
	private static Path filePath;
	
	//LOGGER
	private static Logger LOGGER = Logger.getLogger("RofLogger");

	static {
		LOGGER.getUseParentHandlers();
	}

	//mission methods

	/**
	 * initialization of mission => creation of file, init of list,...
	 * @param fileName : path of the mission file
	 * @param fileName : name of the mission file (not including extension .mission)
	 * @throws IOException 
	 */
	public static void init (String missionPath, String fileName, RofMissionParams params) throws IOException {
		
		//LOGGER.setLevel(Level.FINE);
		
		//update info from params
		precLevel=params.getPrecLevel();
		precType=params.getPrecType();
		turbulence=params.getTurbulence();
		tempPressLevel=params.getPrecLevel();
		temperature = params.getTemperature();
		pressure = params.getPressure();
		// {direction, speed}
		windLayers0[0]=params.getWindDir();
		windLayers0[1]=params.getWindSpeed();
		windLayers500[0]=params.getWindDir();
		windLayers500[1]=params.getWindSpeed();
		windLayers1000[0]=params.getWindDir();
		windLayers1000[1]=params.getWindSpeed();
		
		listMCUtoActivate = new ArrayList<Integer>();
		
		//File deletion
		filePath=Paths.get(missionPath+fileName+".msnbin");
		if (Files.exists(filePath)) Files.delete(filePath);
		filePath=Paths.get(missionPath+fileName+".list");
		if (Files.exists(filePath)) Files.delete(filePath);
		
		//File creation  => default option = TRUNCATE_EXISTING	
		filePath=Paths.get(missionPath+fileName+".Mission");
		currentIndex=1;
		try (BufferedWriter writer = Files.newBufferedWriter(filePath, StandardCharsets.ISO_8859_1)) {
			//header of mission 
			String line="# Mission File Version = 1.0;\r\n\r\n"+
			"Options\r\n"+
			"{\r\n"+
			"  LCName = 0;\r\n"+
			"  LCDesc = 1;\r\n"+
			"  LCAuthor = 2;\r\n"+
			"  PlayerConfig = "+PlanesProp.getScript(params.getPlayerPlaneModel())+";\r\n"+
			"  Time = "+time+";\r\n"+
			"  Date = "+date+";\r\n"+
			"  HMap = \"graphics\\landscape\\height.ini\";\r\n"+
			"  Textures = \"graphics\\landscape\\textures.ini\";\r\n"+
			"  Forests = \"graphics\\landscape\\trees\\woods.wds\";\r\n"+
			"  GuiMap = \"swf\\data\\maps\\05.01.1918.info\";\r\n"+
			"  SeasonPrefix = \""+season+"\";\r\n"+
			"  MissionType = 0;\r\n"+
			"";
			writer.write(line);
			
			// Meteo
			line=	"  CloudLevel = "+cloudLevel+";\r\n"+
					"  CloudHeight = "+cloudHeight+";\r\n"+
					"  PrecLevel = "+precLevel+";\r\n"+
					"  PrecType = "+precType+";\r\n"+
					"  CloudConfig = \"clear\\sky.ini\";\r\n"+
					"  SeaState = 0;\r\n"+
					"  Turbulence = "+turbulence+";\r\n"+
					"  TempPressLevel = "+tempPressLevel+";\r\n"+
					"  Temperature = "+temperature+";\r\n"+
					"  Pressure = "+pressure+";\r\n"+
					"  WindLayers\r\n  {\r\n"+
					"    0 :     "+windLayers0[0]+" :     "+windLayers0[1]+";\r\n"+
					"    500 :     "+windLayers500[0]+" :     "+windLayers500[1]+";\r\n"+
					"    1000 :     "+windLayers1000[0]+" :     "+windLayers1000[1]+";\r\n"+
					"    2000 :     "+windLayers1000[0]+" :     "+windLayers1000[1]+";\r\n"+
					"    5000 :     "+windLayers1000[0]+" :     "+windLayers1000[1]+";\r\n"+
					"  }\r\n";
			writer.write(line);
			
			// Countries
			line=	"  Countries\r\n  {\r\n"+
					"    0 : 0;\r\n"+
					"    101 : 1;\r\n"+
					"    102 : 1;\r\n"+
					"    103 : 1;\r\n"+
					"    104 : 1;\r\n"+
					"    105 : 1;\r\n"+
					"    501 : 2;\r\n"+
					"    502 : 2;\r\n"+
					"    600 : 7;\r\n"+
					"    610 : 3;\r\n"+
					"    620 : 4;\r\n"+
					"    630 : 5;\r\n"+
					"    640 : 6;\r\n"+
					"  }\r\n"+
					"}\r\n";
			writer.write(line);
			
		}
		
		// mission text translation
		Path filePath2=Paths.get(missionPath+fileName+".eng");
		try (BufferedWriter writer = Files.newBufferedWriter(filePath2, StandardCharsets.UTF_16LE)) {
			String line="\uFEFF"+"0:"+fileName+"\r\n"+
				"1:"+params.getBriefing()+"\r\n"+
				"2:lefuneste\r\n";
			writer.write(line);
		}
	}
	
	/**
	 * Write end of mission keyword
	 * @throws IOException
	 */
	public static void end(RofMissionParams params, RofMissObjAirfield airfieldPlayer) throws IOException {
		if (airfieldPlayer != null ) {
			try (BufferedWriter writer = Files.newBufferedWriter(filePath, StandardCharsets.ISO_8859_1,StandardOpenOption.APPEND)) {			
				//generate mission start MCU			
				RofMissObjMCU endMCU = new RofMissObjMCU(currentIndex, params,airfieldPlayer, 0, MCUType.BEGIN, false);		
				for (int MCU : listMCUtoActivate) {
					endMCU.addTarget(MCU);
				}
				int index=endMCU.writeToFile(filePath,endMCU.getIndexInfile(), 0);	
				
				writer.write("# end of file\r\n");
			}
		}
	}
	
	
	/**
	 * write groups and associated elements (airfield&blocks) corresponding to the airfieldselected table in DB
	 * @param request : request which give elements to write: must return indexInFile, fileID 
	 * @return
	 * @throws Exception 
	 */
	public static RofMissObjAirfield writeGroups(String request) throws Exception {
		LOGGER.fine("Group request " + request);
		RofMissObjAirfield result=null;
		
		//retrieve Arfield Groups IDs
		List<RofObjRef> groupRefList = RofDB.findRofObj(request);
		
		//write each group and its associate objects
		for (RofObjRef groupRef : groupRefList) {
			RofMissObjGroup group = new RofMissObjGroup();
			group.readFromDB(RofDB.getCon(), groupRef.getObjID(), groupRef.getFileID());
			currentIndex=group.writeToFile(filePath,currentIndex);
			LOGGER.fine("Group " + group.getIndexInfile() + " written into Mission File");
			
			//write each Airfield element of the group
			String requestAirfield="select indexInfile, fileID from Airfield where fileID="+groupRef.getFileID()+" and groupID="+groupRef.getObjID();
			List<RofObjRef> airfieldRefList = RofDB.findRofObj(requestAirfield);
			
			if (airfieldRefList.size()>0)
			{
				result=new RofMissObjAirfield();
				result.readFromDB(RofDB.getCon(),(airfieldRefList.get(0)).getObjID() , (airfieldRefList.get(0)).getFileID());
			}
			
			for (RofObjRef airfieldRef : airfieldRefList) {	
				RofMissObjAirfield airfield=new RofMissObjAirfield();
				airfield.readFromDB(RofDB.getCon(),airfieldRef.getObjID() , airfieldRef.getFileID());
				currentIndex=airfield.writeToFile(filePath, currentIndex,0);
				LOGGER.fine("Arfield in group " + airfield.getIndexInfile() + " written into Mission File");
			}
			
			//write each block element of the group
			String requestBlocks="select indexInfile, fileID from RofBlock where fileID="+groupRef.getFileID()+" and groupID="+groupRef.getObjID();
			List<RofObjRef> blockdRefList = RofDB.findRofObj(requestBlocks);
			
			for (RofObjRef blockRef : blockdRefList) {
				RofMissObjBlock block=new RofMissObjBlock();
				block.readFromDB(RofDB.getCon(),blockRef.getObjID() , blockRef.getFileID());
				currentIndex=block.writeToFile(filePath, currentIndex,0);
				LOGGER.fine("Block in group " + block.getIndexInfile() + " written into Mission File");
			}
			//write end of group
			group.writeEndToFile(filePath);		
		}
		return result;
	}
	
	/**
	 * write Airfield not in groups corresponding to the airfieldselected table in DB
	 * @param request : request which give elements to write: must return indexInFile, fileID 
	 * @return
	 * @throws Exception 
	 */
	 public static RofMissObjAirfield writeAirfieldSolo(String request) throws Exception {
		 
		 LOGGER.fine("Airfield solo request " + request);
		 RofMissObjAirfield result=null;
		 
		//retrieve Arfield Groups IDs
		List<RofObjRef> airfieldList = RofDB.findRofObj(request);
		
		if (airfieldList.size()>0) {
			result=new RofMissObjAirfield();
			result.readFromDB(RofDB.getCon(),(airfieldList.get(0)).getObjID() , (airfieldList.get(0)).getFileID());
		}
		
		//write each airfield
		for (RofObjRef airfieldRef : airfieldList) { 
				RofMissObjAirfield airfield=new RofMissObjAirfield();
				airfield.readFromDB(RofDB.getCon(),airfieldRef.getObjID() , airfieldRef.getFileID());
				currentIndex=airfield.writeToFile(filePath, currentIndex,0);
				LOGGER.fine("Airfield solo " + airfield.getIndexInfile() + " written into Mission File");
			}
		return result;
	}
	
	/**
	 * write Blocks not in groups corresponding to the airfieldselected table in DB
	 * @param request : request which give elements to write: must return indexInFile, fileID 
	 * @return
	 * @throws Exception 
	 */
	public static void writeBlocksSolo(String request) throws Exception {
		
		LOGGER.fine("Block solo request " + request);
		//retrieve Arfield Groups IDs
		List<RofObjRef> blockdRefList = RofDB.findRofObj(request);
		
		//write each Block
		for (RofObjRef blockRef : blockdRefList) { 
			RofMissObjBlock block=new RofMissObjBlock();
			block.readFromDB(RofDB.getCon(),blockRef.getObjID() , blockRef.getFileID());
			currentIndex=block.writeToFile(filePath, currentIndex,0);
			LOGGER.fine("Block solo " + block.getIndexInfile() + " written into Mission File");
			}
	}
	
	/**
	 * write plane and associated MCU in mission file
	 * @param index : index of next object in mission file
	 * @param params : parameters for mission generation
	 * @param type : type of airplane use (landing, takeoff,...)
	 * @param parent : parent object of Airplane (position of object taken from object and params)
	 * @return
	 * @throws IOException
	 */
	public static RofMissAirplane writePlane(int index, RofMissionParams params, AirplaneUse type, RofMissObjAirfield parent) throws IOException {
		//write Plane
		RofMissAirplane airplane= new RofMissAirplane(index, params, type, parent);
		index=airplane.writeToFile(filePath, index, index+1);
		
		RofMissObjMCU TRMCU = new RofMissObjMCU(index, airplane, MCUType.TR, true);
		index=TRMCU.writeToFile(filePath,TRMCU.getIndexInfile(), 0);
		return airplane;
	}
	
	
	/**
	 * write flight plane and associated MCU in mission file
	 * @param index : index of next object in mission file
	 * @param params : parameters for mission generation
	 * @param type : type of airplane use (landing, takeoff,...)
	 * @param parent : parent object of Airplane (position of object taken from object and params)
	 * @return
	 * @throws IOException
	 */
	public static RofMissAirplane writePlane(int index, RofMissionParams params, AirplaneUse type, RofMissAirplane parent, double dist, int numberInformation) throws IOException {
		//write Plane
		RofMissAirplane airplane= new RofMissAirplane(index, params, type, parent, numberInformation);
		index=airplane.writeToFile(filePath, index, index+1);
		
		RofMissObjMCU TRMCU = new RofMissObjMCU(index, airplane, MCUType.TR, true);
		TRMCU.addTarget(parent.getIndexInfile()+1);
		index=TRMCU.writeToFile(filePath,TRMCU.getIndexInfile(), 0);
		return airplane;
	}
	
	
	/**
	 * write Landing plane and associated Waypoint/landing MCU in mission file
	 * @param index : number of the first plane to create
	 * @param params : parameters for missions
	 * @param parent : parent Airfield (used to retrieve name and coordinates)
	 * @return
	 * @throws IOException
	 */
	public static int writeLandingPlane(int index, RofMissionParams params, RofMissObjAirfield parent) throws IOException {

		int indexOfLeadPlane=index;
		RofMissAirplane leadPlane=null;
		RofMissAirplane squadPlane=null;
		
		if (params.getNumberOfPlaneLanding() >0 ) {
			//create plane
			leadPlane=RofMission.writePlane(index, params, AirplaneUse.LAND, parent);
			index+=2;
			
			//create Landing point
			RofMissObjMCU LANDMCU = new RofMissObjMCU(index, params, parent,indexOfLeadPlane, MCUType.LAND, false);
			LANDMCU.addObjects(indexOfLeadPlane+1);
			index=LANDMCU.writeToFile(filePath,LANDMCU.getIndexInfile(), 0);
			int indexMUCLand=index-1;
			
			//Create MCU INGRESS Waypoint
			RofMissObjMCU ingressMCU=new RofMissObjMCU(index, params, parent, indexOfLeadPlane, MCUType.INGRESS, false);
			ingressMCU.addObjects(indexOfLeadPlane+1);
			ingressMCU.addTarget(indexMUCLand);
			index=ingressMCU.writeToFile(filePath,ingressMCU.getIndexInfile(), 0);
			listMCUtoActivate.add(index-1);
			
			//Add flight planes
			
			double dist=params.getLandingSeparation();
			for (int i=1;i<params.getNumberOfPlaneLanding();i++) {
				squadPlane=RofMission.writePlane(index, params, AirplaneUse.LAND, leadPlane, dist, i);
				dist+=params.getLandingSeparation();
				index+=2;
			}
		}
		return index;
	}
	
	/**
	 * write takeoff plane and associated takeoff MCU / waypoints in mission file
	 * @param index : number of the first plane to create
	 * @param params : parameters for missions
	 * @param parent : parent Airfield (used to retrieve name and coordinates)
	 * @return
	 * @throws IOException
	 */
	public static int writeTakeOffPlane(int index, RofMissionParams params, RofMissObjAirfield parent) throws IOException {

		int indexOfLeadPlane=index;
		RofMissAirplane leadPlane=null;
		RofMissAirplane squadPlane=null;
		
		if (params.getNumberOfPlaneTakeoff() >0 ) {
					
			//create plane
			leadPlane=RofMission.writePlane(index, params, AirplaneUse.TAKEOFF, parent);
			int indexTakeoff=index+2;
			//int indexAlt=indexTakeoff+1;
			//int indexEgress=indexAlt+1;
			int indexEgress=indexTakeoff+1;
			int indexDeactivate=indexEgress+1;
		
			//Create MCU ALT Waypoint
			//RofMissObjMCU altMCU=new RofMissObjMCU(indexAlt, params, parent, indexOfLeadPlane, MCUType.ALT, false);
			//altMCU.addObjects(indexOfLeadPlane+1);
			
			//Create MCU EGRESS Waypoint
			RofMissObjMCU egressMCU=new RofMissObjMCU(indexEgress, params, parent, indexOfLeadPlane, MCUType.EGRESS, false);
			egressMCU.addObjects(indexOfLeadPlane+1);
			egressMCU.addTarget(indexDeactivate);
			
			//Create MCU DEACTIVATE Waypoint
			RofMissObjMCU deactivateMCU=new RofMissObjMCU(indexDeactivate, params, parent, indexOfLeadPlane, MCUType.DEACTIVATE, false);
			deactivateMCU.addObjects(indexOfLeadPlane+1);
	
			//Point ALT waypoint to EGRESS Waypoint
			//altMCU.addTarget(egressMCU.getIndexInfile());
			//altMCU.addObjects(indexOfLeadPlane+1);
			
			//create Takeoff point
			RofMissObjMCU takeOffMCU = new RofMissObjMCU(indexTakeoff, params, parent, indexOfLeadPlane, MCUType.TAKEOFF, false);
			takeOffMCU.addObjects(indexOfLeadPlane+1);
			
			//writing of MCU
			index=takeOffMCU.writeToFile(filePath,takeOffMCU.getIndexInfile(), 0);
			//index=altMCU.writeToFile(filePath,altMCU.getIndexInfile(), 0);
			index=egressMCU.writeToFile(filePath,egressMCU.getIndexInfile(), 0);
			index=deactivateMCU.writeToFile(filePath,deactivateMCU.getIndexInfile(), 0);
			
			listMCUtoActivate.add(takeOffMCU.getIndexInfile());
	
			
			//Add flight planes
			double dist=params.getLandingSeparation();
			for (int i=1;i<params.getNumberOfPlaneTakeoff();i++) {
				squadPlane=RofMission.writePlane(index, params, AirplaneUse.TAKEOFF, leadPlane, 0, i);
				index+=2;
			}
		}
		
		return index;
	}
	
	
	/**
	 * Generate mission files
	 * @param missionPath : Path to the mission
	 * @param fileName : name of the mission file (without extension)
	 * @param fileID : file IDs to use to select objects to add
	 * @throws Exception 
	 */
	public static void generate(String missionPath, String fileName, List <Integer> fileID, RofMissionParams params) throws Exception {
		
		RofMissObjAirfield airfieldPlayer=null;
		RofMission.init(missionPath, fileName, params);
		
		//generate fileID for request
		String fileIDString="("+fileID.get(0);
		for (int i=1;i<=fileID.size()-1;i++) {
			fileIDString+=","+fileID.get(i);
		}
		fileIDString+=")";
		
		//retrieve and write Airfield groups
		String request="select A.groupID as IndexInfile, A.fileID from Airfield as A join AIRFIELDSELECTED as S on ( A.indexInFile=S.airfieldID and A.fileID=S.fileID)  where groupID != -1 AND A.fileID IN "+fileIDString+" group by A.groupId, A.fileID";
		RofMissObjAirfield airfield1=RofMission.writeGroups(request);
		
		// generate airfield list from request
		
		//retrieve and write Blocks groups
		request="select groupID as IndexInFile, fileID from rofBlock where "+
				"XPos >= (select MIN(A.XPOS)-"+params.getXmargin()+" from Airfield as A join AIRFIELDSELECTED as S on (A.indexInFile=S.airfieldID and A.fileID=S.fileID)) AND "+
				"XPos <= (select MAX(A.XPOS)+"+params.getXmargin()+" from Airfield as A join AIRFIELDSELECTED as S on (A.indexInFile=S.airfieldID and A.fileID=S.fileID)) AND "+
				"Zpos >= (select MIN(A.ZPOS)-"+params.getZmargin()+" from Airfield as A join AIRFIELDSELECTED as S on (A.indexInFile=S.airfieldID and A.fileID=S.fileID)) AND "+
				"Zpos <= (select MAX(A.ZPOS)+"+params.getZmargin()+" from Airfield as A join AIRFIELDSELECTED as S on (A.indexInFile=S.airfieldID and A.fileID=S.fileID)) AND "+
				"groupID <> -1 AND "+"fileID in "+fileIDString+" "+
				"group by groupID, fileID"+
				 ";";
		RofMission.writeGroups(request);
		
		//retrieve and write Airfields not in groups
		request="select A.IndexInfile, A.fileID from Airfield as A join AIRFIELDSELECTED as S on ( A.indexInFile=S.airfieldID and A.fileID=S.fileID)  where groupID = -1 AND A.fileID IN "+fileIDString+" group by A.indexInFile, A.fileID";
		RofMissObjAirfield airfield2=RofMission.writeAirfieldSolo(request);
		
		//retrieve and write Blocks not in groups
		request="select indexInFile, fileID, name from rofBlock where "+
				"XPos >= (select MIN(A.XPOS)-"+params.getXmargin()+" from Airfield as A join AIRFIELDSELECTED as S on ( A.indexInFile=S.airfieldID and A.fileID=S.fileID)) AND "+
				"XPos <= (select MAX(A.XPOS)+"+params.getXmargin()+" from Airfield as A join AIRFIELDSELECTED as S on ( A.indexInFile=S.airfieldID and A.fileID=S.fileID)) AND "+
				"Zpos >= (select MIN(A.ZPOS)-"+params.getZmargin()+" from Airfield as A join AIRFIELDSELECTED as S on ( A.indexInFile=S.airfieldID and A.fileID=S.fileID)) AND "+
				"Zpos <= (select MAX(A.ZPOS)+"+params.getZmargin()+" from Airfield as A join AIRFIELDSELECTED as S on ( A.indexInFile=S.airfieldID and A.fileID=S.fileID)) AND "+
				"groupID = -1 AND "+"fileID in "+fileIDString+" "+
				 ";";
		RofMission.writeBlocksSolo(request);	
		
		RofMissObjAirfield fakeAifield=null;
		
		//Write player plane
		if (airfield1 != null) airfieldPlayer = airfield1;
		if (airfield2 != null) airfieldPlayer = airfield2;
		
		RofMission.writePlane(currentIndex, params, AirplaneUse.PLAYER, airfieldPlayer);
		currentIndex+=2;
		
		
		//retrieve Airfield infos
		request="select A.IndexInfile, A.fileID from AIRFIELDSELECTED as S join Airfield as A on (A.indexInFile=S.airfieldID and A.fileID=S.fileID) where A.filtered like 'false'";
		List<RofObjRef> airfieldList = RofDB.findRofObj(request);
		
		//write each airfield
		for (RofObjRef airfieldRef : airfieldList) { 	
			RofMissObjAirfield airfield=new RofMissObjAirfield();
			airfield.readFromDB(RofDB.getCon(),airfieldRef.getObjID() , airfieldRef.getFileID()); 
			//airfield.readFromDB(RofDB.getCon(), 651 , 0);
			
			//Write landing plane
			currentIndex=RofMission.writeLandingPlane(currentIndex, params, airfield);
			
			//Write takeoff plane if no downwind takeoff
			if ( params.isAirfieldReversed(airfield)) {
				currentIndex=RofMission.writeTakeOffPlane(currentIndex, params, airfield);
			}
			
		}
		
		//write mission end keywords
		RofMission.end(params, airfieldPlayer);
		
		//mission log processing
		//MissionLog missionLog=new MissionLog("F:\\TEMP\\RoF\\missionReport(2014-11-08_12-44-41)[0].txt");
		
		
	}
		
	
}
